package com.chenyun.store.aws;

import cn.hutool.core.io.IoUtil;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;

import com.chenyun.common.exceptions.BusinessException;
import com.chenyun.store.pojo.StoreDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;

/**
 * aws 3 协议上传
 *
 * @author: Lonni
 * @date: 2023/11/6 0006 11:32
 */
@Slf4j
public class Aws3ClientService implements IOssClientService {

    private final StoreDTO configDTO;

    private final AmazonS3 client;


    public Aws3ClientService(StoreDTO dto) {
        this.configDTO = dto;

        try {
            AwsClientBuilder.EndpointConfiguration endpointConfig =
                    new AwsClientBuilder.EndpointConfiguration(dto.getEndpoint(), dto.getRegion());

            AWSCredentials credentials = new BasicAWSCredentials(dto.getAccessKey(), dto.getAccessKey());
            AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials);
            ClientConfiguration clientConfig = new ClientConfiguration();
            if (dto.getHttps().booleanValue()) {
                clientConfig.setProtocol(Protocol.HTTPS);
            } else {
                clientConfig.setProtocol(Protocol.HTTP);
            }
            AmazonS3ClientBuilder build = AmazonS3Client.builder()
                    .withEndpointConfiguration(endpointConfig)
                    .withClientConfiguration(clientConfig)
                    .withCredentials(credentialsProvider)
                    .disableChunkedEncoding();
            if (!StringUtils.containsAny(configDTO.getEndpoint(), CLOUD_SERVICE)) {
                // minio 使用https限制使用域名访问 需要此配置 站点填域名
                build.enablePathStyleAccess();
            }
            this.client = build.build();

            createBucket();
        } catch (Exception e) {
            throw e;
        }


    }


    void createBucket() {
        try {
            String bucketName = configDTO.getBucket();
            if (client.doesBucketExistV2(bucketName)) {
                return;
            }
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            AccessPolicyType accessPolicy = getAccessPolicy();
            createBucketRequest.setCannedAcl(accessPolicy.getAcl());
            client.createBucket(createBucketRequest);
            client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
        } catch (Exception e) {
            log.error("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]");
            e.printStackTrace();
        }
    }

    /**
     * 获取当前桶权限类型
     *
     * @return 当前桶权限类型code
     */
    private AccessPolicyType getAccessPolicy() {
        return AccessPolicyType.getByType(configDTO.getAccessPolicy());
    }

    private static String getPolicy(String bucketName, PolicyType policyType) {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
        if (policyType == PolicyType.WRITE) {
            builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n");
        } else if (policyType == PolicyType.READ_WRITE) {
            builder.append("\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n");
        } else {
            builder.append("\"s3:GetBucketLocation\"\n");
        }
        builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("\"\n},\n");
        if (policyType == PolicyType.READ) {
            builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
            builder.append(bucketName);
            builder.append("\"\n},\n");
        }
        builder.append("{\n\"Action\": ");
        switch (policyType) {
            case WRITE:
                builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
                break;
            case READ_WRITE:
                builder.append("[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n");
                break;
            default:
                builder.append("\"s3:GetObject\",\n");
                break;
        }
        builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n");
        return builder.toString();
    }

    /**
     * 上传文件
     *
     * @param data
     * @param path
     * @param contentType
     * @return
     */
    @Override
    public String upload(byte[] data, String path, String contentType) {
        return upload(new ByteArrayInputStream(data), path, contentType);
    }

    /**
     * 获取文件信息
     *
     * @param path
     * @return
     */
    @Override
    public InputStream getObjectContent(String path) {
        path = path.replace(getUrl(configDTO, "") + "/", "");
        S3Object object = client.getObject(configDTO.getBucket(), path);
        return object.getObjectContent();
    }

    @Override
    public String upload(InputStream inputStream, String path, String contentType) {
        if (!(inputStream instanceof ByteArrayInputStream)) {
            inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
        }
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            if (StringUtils.isNotBlank(contentType)){
                metadata.setContentType(contentType);
            }
            metadata.setContentLength(inputStream.available());
            PutObjectRequest putObjectRequest = new PutObjectRequest(configDTO.getBucket(), path, inputStream, metadata);
            // 设置上传对象的 Acl 为公共读
            putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
            client.putObject(putObjectRequest);
        } catch (Exception e) {
            throw new BusinessException("上传文件失败，请检查配置信息:[" + e.getMessage() + "]");
        }
        return getUrl(configDTO, path);
    }

    @Override
    public String upload(File file, String path) {

        try {
            PutObjectRequest objectRequest = new PutObjectRequest(configDTO.getBucket(), path, file);
            objectRequest.setCannedAcl(getAccessPolicy().getAcl());
            PutObjectResult putObjectResult = client.putObject(objectRequest);
            return getUrl(configDTO, path);
        } catch (Exception exception) {
            throw new BusinessException("上传文件失败，请检查配置信息:[" + exception.getMessage() + "]");
        }
    }

    @Override
    public void delete(String path) {
        path = path.replace(getUrl(configDTO, "") + "/", "");
        try {
            client.deleteObject(configDTO.getBucket(), path);
        } catch (Exception e) {
            throw new BusinessException("删除文件失败，请检查配置信息:[" + e.getMessage() + "]");
        }
    }
}



